package com.rfid.socketio;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.rfid.DirectoryBean;
import com.rfid.sockethelper.SocketBase;
import com.rfid.util.LogUtils;
import com.rfid.webp2plib.AppUtils;
import com.rfid.webp2plib.SignalClient;
import com.rfid.webp2plib.User;
import java.io.DataInputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

public class SocketIOClient extends SocketBase{

    private static final String TAG = "SocketIOServer";
    private SignalClient.UserListener mUserListener;
    private final static String mSocketAddress = "http://2.tcp.cpolar.cn:12434";
//    private final static String mSocketAddress = "http://192.168.2.13:3000";
    private io.socket.client.Socket mClient;
    private String mClientId;
    OutReadableStream mOut = new OutReadableStream();
    Lock mLock = new ReentrantLock();
    Map<String,RemoteDevice> mMap = new HashMap<>();
    Executor mExecutor = Executors.newCachedThreadPool();
    Handler mHandler = new Handler();
    boolean running = true;
    boolean isServer;
    public SocketIOClient(OnReceiveListener onReceiveListener) {
        super(onReceiveListener);
        LogUtils.v("mSocketAddress = " +mSocketAddress);
        try {
            mClient = IO.socket(mSocketAddress);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        mClient.on("login",mLoginListener);
        mClient.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                mClientId = mClient.id();
                LogUtils.v("EVENT_CONNECT = " + mClientId);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mUserListener != null) {
                            mUserListener.onConnect(mClientId);
                        }
                    }
                });
            }
        });
        mClient.on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                System.out.println("EVENT_DISCONNECT");
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mUserListener != null) {
                            mUserListener.onDisconnect();
                        }
                    }
                });
            }
        });
        mClient.on("login",mLoginListener);
        mClient.on("get_logins",mLoginListener);
        mClient.on("turnMessage", new Emitter.Listener() {
            @Override
            public void call(Object... args) {
                try {
                    String id = (String) args[0];
//                    if (TextUtils.isEmpty(ip)) {
                        setRemoteIp(id);
//                    }
//                    ByteArrayInputStream in = new ByteArrayInputStream((byte[]) args[1]);
//                    DataInputStream din = new DataInputStream(in);
//                    short cmd = din.readShort();
                    RemoteDevice device = mMap.get(id);
                    if (device == null) {
                        device = new RemoteDevice(id);
                        mMap.put(id,device);
                        startThread(device);
                    }
                    device.in.writeBytes((byte[]) args[1]);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        init(mOut,"",mLock);
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (running) {
                    try {
                        //buffer为20kb，等待100ms意味着1秒发送10次，速度为 10*20KB=200KB/S
                        Thread.sleep(100);
                        int size = mOut.size();
                        if (size > 0) {
                            byte buffer[] = new byte[size];
                            for (int i=0;i<size;i++) {
                                int b = mOut.read();
                                buffer[i] = (byte) (b&0xff);
                            }
                            sendMessage(ip,buffer);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public void connectSignalServer() {
        LogUtils.v("connectSignalServer");
        mClient.connect(); //onLogin
    }

    private Emitter.Listener mLoginListener = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            try {
                String json = args[0].toString();
                LogUtils.d(TAG, "mLoginListener json : " + json);
                Gson gson = new Gson();
                List<User> users = gson.fromJson(json, new TypeToken<List<User>>() {
                }.getType());

                List<String> ids = new ArrayList<>();
                User me = null;
                for (User user : users) {
                    if (mClient.id().equals(user.getId())) {
                        me = user;
                        break;
                    } else {
                        ids.add(user.getId());
                    }
                }
                if (me != null) {
                    users.remove(me);
                }
                List<String> rms = new ArrayList<>();
                Set<String> sets = mMap.keySet();
                List<String> keys = new ArrayList<>(sets);
                for (String id : keys) {
                    if (!ids.contains(id)) {
                        mMap.remove(id);
                    }
                }
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (mUserListener != null) {
                            mUserListener.onLogin(mClientId, users);
                        }
                    }
                });
            } catch (Exception e) {
                LogUtils.e(e);
            }
        }
    };

    public void login(Context context,String extra) {
        User.DataBean dataBean = new User.DataBean();
        dataBean.setVer(AppUtils.getVersionName(context));
        dataBean.setCode(AppUtils.getVersionCode(context)+"");
        dataBean.setPkg(AppUtils.getPackageName(context));
        dataBean.setName(AppUtils.getAppName(context));
        dataBean.setExtraInfo(extra);
        Gson gson = new Gson();
        mClient.emit("login", gson.toJson(dataBean));
    }

    public void getLogins() {
        mClient.emit("get_logins");
    }

    public String getClientId() {
        return mClientId;
    }

    public boolean connected() {
        return mClient.connected();
    }

    public void setUserListener(SignalClient.UserListener listener) {
        mUserListener = listener;
    }

    private void startThread(RemoteDevice in) {
        mExecutor.execute(in);
    }


    public void close(String id) {
        RemoteDevice device = mMap.get(id);
        if (device != null) {
            device.close();
        }
    }

    public void release() {
        LogUtils.v("release");
        mUserListener = null;
        running = false;
        mClient.close();
        for (String id : mMap.keySet()) {
            close(id);
        }
        mMap.clear();
    }

    public void sendMessage(String id,byte buffer[]) {
        mClient.emit("turnMessage",id,buffer);
    }

    @Override
    protected void sendFileListInfo(String ip, DirectoryBean bean) {
        setRemoteIp(ip);
        sendFileListInfo(bean);
    }

    @Override
    protected void sendFile(String ip, String path) {
        setRemoteIp(ip);
        sendFile(path);
    }

    class RemoteDevice implements Runnable{
        InWriteableStream in;
        String id;
        boolean loop = true;

        public RemoteDevice(String id) {
            this.in = new InWriteableStream();
            this.id = id;
        }

        public void close() {
            loop = false;
        }

        public void run() {
            while (loop) {
                try {
                    DataInputStream din = new DataInputStream(in);
                    short cmd = din.readShort();
                    String text = SocketIOClient.this.receive(din, id, cmd);
//                    System.out.println("receive id = " + id + "," + Thread.currentThread().getName());
                    if (text == null || text.length() == 0) {
                        continue;
                    }
                    postToUI(id, cmd, text);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
